#include "rcpsp.h"

RCPSP::RCPSP(){
	string aux = "config.txt";
	loadConfig(aux);
	readData();
}
//-------------------------------------------------------------------------------------------------
void RCPSP::loadConfig(string file){
	ifstream archi;
	archi.open(file.c_str());
	if(archi.is_open()){
		string str_line;
		char* line;
		//Cantidad de proyectos
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nProj = atoi(line);
		//tasa de cruza
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->tCross = atof(line);
		//tasa de mutacion
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->tMut = atof(line);
		//fitness deseado
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->desFitness = atoi(line);
		//Tipo de seleccion
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->tSelec = atoi(line);
		//numero de individuos en la competencia
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nComp = atoi(line);
		//Cantidad maxima de generaciones
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->maxGenerations = atoi(line);
		//Cantidad de recursos
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->nRes = atoi(line);
		//Duracion deseada del proyecto
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->desFinalDuration = atoi(line);
		}		
	archi.close();
	this->vProject.resize(this->nProj);
	this->sortIndx.resize(this->nProj);
	this->vResources.resize(this->nRes);
	this->vResources[0] = 24;
	this->vResources[1] = 23;
	this->vResources[2] = 25;
	this->vResources[3] = 33;
	srand(time(NULL));	
}
//-------------------------------------------------------------------------------------------------
int RCPSP::find(int p,int x){
	for(int i=0;i<this->nTask;i++){
		if(this->vProject[p].vTask[i] == x) return -1;
	}
	return 0;
}
//-------------------------------------------------------------------------------------------------
void RCPSP::initRandomPopulation(){
	for(int i=0;i<this->nProj;i++){
		for(int j=0;j<this->nTask;j++){
			int indx = rand() % this->nTask;
			while(find(i,indx) == -1) indx = rand() % this->nTask;
			this->vProject[i].vTask[j] = indx;
		}
	}
}
//-------------------------------------------------------------------------------------------------
void RCPSP::printProject(){
	for(int i=0;i<this->nProj;i++){
		cout<<"Proyecto ["<<i<<"]: "<<endl;
		for(int j=0;j<this->nTask;j++){
			cout<<this->vProject[i].vTask[j]<<" ";
		}
		cout<<endl<<endl;
		cout<<"Tiempo requerido: "<<this->vProject[i].finalDuration<<endl;
	}	
}
//-------------------------------------------------------------------------------------------------
void RCPSP::printBestFitness(){
	cout<<"Proyecto ["<<this->indMin<<"]: "<<endl;
	for(int j=0;j<this->nTask;j++) cout<<this->vProject[this->indMin].vTask[j]<<" ";
	cout<<endl;
	cout<<"Tiempo requerido: "<<this->vProject[this->indMin].finalDuration<<endl;
}
//-------------------------------------------------------------------------------------------------
void RCPSP::evolution(){
	initRandomPopulation();
	//printProject();
	int generation = 0;
	getFitness();
	//createData(50);
	time_t start = clock();
	while((this->minFitness != this->desFitness) && (generation < this->maxGenerations)){
		cout<<"Generacion: "<<generation<<" ";
		selectNextPopulation();
		cross();
		mutation();
		getFitness();
		cout<<"Fitness minimo: "<<this->minFitness<<endl;
		generation++;
	}
	if(this->minFitness == this->desFitness){
		cout<<"Se ha resuelto el problema."<<endl;
		printBestFitness();
		cout<<endl<<"Tiempo total para la resolucion: "<<(clock()-start)/(double)CLOCKS_PER_SEC<<" segundos."<<endl;
	}
	else cout<<"No se ha resuelto el problema."<<endl;
}
//-------------------------------------------------------------------------------------------------
bool notin(int x,vector<int>& v){
	for(unsigned int i=0;i<v.size();i++){
		if(x == v[i]) return false;
	}
	return true;
}
//-------------------------------------------------------------------------------------------------
int repeat(vector<int>& v){
	vector<int> aux;	
	for(unsigned int i=0;i<v.size();i++){
		if(notin(v[i],aux)) aux.push_back(v[i]);
	}
	return v.size()-aux.size();
}
//-------------------------------------------------------------------------------------------------
int RCPSP::getFitness(){
    this->minFitness = INT_MAX;
		int fit;
    for(int i=0;i<this->nProj;i++){
			fit = 0;
			//Reviso si se cumplen antecesoras y armamos vector paralelos
			vector<int> vParalel;
			for(int j=0;j<this->nTask;j++){
				int indx = this->vProject[i].vTask[j];	//Tarea escogida
				int lenght = this->vProjTask[indx].prevTasks.size();
				if(!lenght) vParalel.push_back(indx);
				else{
					bool flag = false;
					for(int k=0;k<lenght;k++){
						int auxPrevTask = this->vProjTask[indx].prevTasks[k], c = 0;
						for(int l=0;l<j;l++){
							if(auxPrevTask == this->vProject[i].vTask[l]){ 
								c++;
								break;
							}
						}
						if(!c) fit++;	//En caso de que la tarea previa no haya sido ejecutada	
						else{
							for(int l=vParalel.size()-1;l>=0;l--){
								if(auxPrevTask == vParalel[l]) {flag = true; break;}
								if(vParalel[l]==-1) break;
							}
						}
					}
					if(flag){ vParalel.push_back(-1); vParalel.push_back(indx);}
					else vParalel.push_back(indx);
				}
			}
			fit += repeat(this->vProject[i].vTask)*3;		//Penalizo en caso de existir repeticiones
			//cout<<fit<<endl;

			//Reviso que las tareas dispongan de los recursos que necesitan para ejecutarse
			int resPen = 0;
			for(unsigned int j=0;j<vParalel.size();j++){
				if(vParalel[j]!=-1){
					for(int k=0;k<this->nRes;k++){
						this->vResources[k] -= this->vProjTask[vParalel[j]].resources[k];
					}
				}
				else{
						for(int j=0;j<this->nRes;j++){ 
							if(this->vResources[j]<0) resPen += abs(this->vResources[j]);
						}
						//recursos renovables
						this->vResources[0] = 24;
						this->vResources[1] = 23;
						this->vResources[2] = 25;
						this->vResources[3] = 33;
				}
			}	

			fit += resPen;
			//cout<<fit<<endl;

			//Calculo del makespan
			this->vProject[i].finalDuration = 0;
			int deltaT = this->vProjTask[vParalel[0]].duration;	//tiempo por conjunto de tareas paralelas
			for(unsigned int j=1;j<vParalel.size();j++){
				if(vParalel[j] != -1){
					//busca la actividad de mas tiempo entre las paralelas
					if(this->vProjTask[vParalel[j]].duration > deltaT) deltaT = this->vProjTask[vParalel[j]].duration;	
				}
				else{
					this->vProject[i].finalDuration += deltaT;	//incrementa el tiempo del projecto
					deltaT = -1;
				}
			}
			this->vProject[i].finalDuration += deltaT;
			//fit += abs(this->vProject[i].finalDuration - this->desFinalDuration) * 2;

			//Fitness final
			this->fitness[i] = fit; 
			if(fit < this->minFitness){
				this->minFitness = fit;
				this->indMin = i;
				//if(this->minFitness == 0) {for(int p=0;p<vParalel.size();p++) cout<<vParalel[p]<<" "; cout<<endl;}
			}
			//cin.get();
		}
		return fit;
}
//-------------------------------------------------------------------------------------------------
void RCPSP::cross(){
	int crossPoint1,crossPoint2;
	double prob;
	bool flag;
	int aux;
	for(int i=0;i<this->nProj;i+=2){
		prob = rand()/(double)RAND_MAX ;
		if(prob < this->tCross){
			crossPoint1 = rand() % this->nTask;
			crossPoint2 = rand() % this->nTask;
			if(crossPoint1 < crossPoint2){
				for(int j=crossPoint1;j<=crossPoint2;j++){
					flag = false;
					aux = this->vProject[i].vTask[j];
					for(int k=0;k<crossPoint1;k++){
						if(aux == this->vProject[i+1].vTask[k]){flag = true; break;}
					}
					if(!flag){
						for(int k=crossPoint2;k<this->nTask;k++){
							if(aux == this->vProject[i+1].vTask[k]){flag = true; break;}
						}
						if(!flag){
							flag = false;
							for(int k=0;k<crossPoint1;k++){
								if(this->vProject[i+1].vTask[j] == this->vProject[i].vTask[k]){flag = true; break;}
							}
							if(!flag){
								for(int k=crossPoint2;k<this->nTask;k++){
									if(this->vProject[i+1].vTask[j] == this->vProject[i].vTask[k]){flag = true; break;}
								}
								if(!flag){
									this->vProject[i].vTask[j] = this->vProject[i+1].vTask[j];
									this->vProject[i+1].vTask[j] = aux;
								}
							}
						}
					}
				}
			}
			if(crossPoint2 < crossPoint1){
				for(int j=crossPoint2;j<=crossPoint1;j++){
					flag = false;
					aux = this->vProject[i].vTask[j];
					for(int k=0;k<crossPoint2;k++){
						if(aux == this->vProject[i+1].vTask[k]){flag = true; break;}
					}
					if(!flag){
						for(int k=crossPoint1;k<this->nTask;k++){
							if(aux == this->vProject[i+1].vTask[k]){flag = true; break;}
						}
						if(!flag){
							flag = false;
							for(int k=0;k<crossPoint2;k++){
								if(this->vProject[i+1].vTask[j] == this->vProject[i].vTask[k]){flag = true; break;}
							}
							if(!flag){
								for(int k=crossPoint1;k<this->nTask;k++){
									if(this->vProject[i+1].vTask[j] == this->vProject[i].vTask[k]){flag = true; break;}
								}
								if(!flag){
									this->vProject[i].vTask[j] = this->vProject[i+1].vTask[j];
									this->vProject[i+1].vTask[j] = aux;
								}
							}
						}
					}
				}
			}
		}
	}
}
//-------------------------------------------------------------------------------------------------
void RCPSP::mutation(){
	for(int i=0;i<this->nProj;i++){
		if(rand()/(double)RAND_MAX < this->tMut){
			int mutPoint1 = rand()%this->nTask;
			int mutPoint2 = rand()%this->nTask;
			while(mutPoint1 == mutPoint2) mutPoint2 = rand()%this->nTask;
			int aux = this->vProject[i].vTask[mutPoint1];
			this->vProject[i].vTask[mutPoint1] = this->vProject[i].vTask[mutPoint2];
			this->vProject[i].vTask[mutPoint2] = aux;
		}
	}
}
//-------------------------------------------------------------------------------------------------
void RCPSP::selectNextPopulation(){
	switch(this->tSelec){
		case 0 : {
			roulette();
			break;
		}
		case 1 : {
			competency();
			break;
		}
		case 2 : {
			window();
			break;
		}
	}
}
//-------------------------------------------------------------------------------------------------
void RCPSP::roulette(){}
//-------------------------------------------------------------------------------------------------
void RCPSP::window(){
	vector<project> parents;
	parents.resize(this->nProj);
	sortFitness();
	int j;
	for(int i=0;i<this->nProj;i++){
		if(!i)	j = 0;
		else j = rand() % i;
		parents[i] = this->vProject[this->sortIndx[j]];				
	}
	copy(parents.begin(),parents.end(),this->vProject.begin());
}
//-------------------------------------------------------------------------------------------------
void RCPSP::sortFitness(){
	vector<int> auxFitness = this->fitness;
	for(int i=0;i<this->nProj-1;i++){	
		double min = auxFitness[i];
		int indMin1 = i;	
		for(int j=i+1;j<this->nProj;j++){
			if(auxFitness[j]<min){
				min = auxFitness[j];
				indMin1 = j;			
			}
		}
		auxFitness[indMin1] = auxFitness[i];
		auxFitness[i] = min;
		this->sortIndx[i] = indMin1;
	}
}
//-------------------------------------------------------------------------------------------------
void RCPSP::competency(){
	int indx,aux,fit;
	vector<project> auxProj;
	auxProj.resize(this->nProj);
	for(int i=0;i<this->nProj;i++){
		aux = rand() % this->nProj;
		fit = this->fitness[aux];
		indx = aux;
		for(int j=0;j<this->nComp-1;j++){
			aux = rand() % this->nProj;
			if(fit > this->fitness[aux]){
				fit = this->fitness[aux];
				indx = aux;
			}
		}
		auxProj[i] = this->vProject[indx];
	}
	copy(auxProj.begin(),auxProj.end(),this->vProject.begin());
}
//-------------------------------------------------------------------------------------------------
void RCPSP::readData(){
	int index;
	ifstream archi;
	archi.open("data2.txt");
	if(archi.is_open()){
		string str_line;
		char* line;
		getline(archi,str_line);
		while(!archi.eof()){
			//Nº actividad
			getline(archi,str_line);
			line = &str_line[0];
			index = atoi(line) - 1;
			this->vProjTask.resize(index+1);
			//Duracion
			getline(archi,str_line);
			getline(archi,str_line);
			line = &str_line[0];
			this->vProjTask[index].duration = atoi(line);
			//Prioridad
			getline(archi,str_line);
			getline(archi,str_line);
			line = &str_line[0];
			this->vProjTask[index].priority = atoi(line);
			//Recursos
			getline(archi,str_line);
			getline(archi,str_line);
			line = &str_line[0];
			while(line[0] != '#'){
				this->vProjTask[index].resources.push_back(atoi(line));
				getline(archi,str_line);
				line = &str_line[0];
			}
			//Tareas previas
			getline(archi,str_line);
			line = &str_line[0];
			while(line[0] != '@'){
				this->vProjTask[index].prevTasks.push_back(atoi(line)-1);
				getline(archi,str_line);
				line = &str_line[0];
			}/*
			for(int i=0;i<this->vProjTask[index].prevTasks.size();i++){
				cout<<this->vProjTask[index].prevTasks[i]<<" ";
			}
			cout<<endl; cin.get();*/
			getline(archi,str_line);
		}
	}		
	archi.close();
	this->nTask = this->vProjTask.size();
	for(int i=0;i<this->nProj;i++){ 
		this->vProject[i].vTask.resize(this->nTask);
		for(int j=0;j<this->nTask;j++){ 
			this->vProject[i].vTask[j] = -1; //Para que la actividad cero no este presente y pueda agregarse.
		}
	}
	this->fitness.resize(this->nProj);
}
//-------------------------------------------------------------------------------------------------
void RCPSP::createData(int tam){
	ofstream archi;
	double r,r1,r2,aux;
	archi.open("data1.txt");
	if(archi.is_open()){
		for(int i=0;i<tam;i++){
			archi << "#ACTIVIDAD"<<endl;
			archi<<i+1<<endl;
			archi<<"#DURACION"<<endl;
			//Creo duraciones con mayor de probabilidad hacia el tiempo 1 que el 4.
			r = rand()/(double)RAND_MAX;
			if(r < 0.5) archi<<1<<endl;
			else{
				if(r<0.7) archi<<2<<endl;
				else{
					if(r<0.9) archi<<3<<endl;
					else archi<<4<<endl;
				}
			} 
			archi<<"#PRIORIDAD"<<endl;
			//Lo mismo que en el caso anterior con la prioridad, observar como la prioridad condiciona a los recursos
			if(r < 0.6){ 
				archi<<1<<endl;
				r1 = rand() % 2;
				r2 = rand() % int(tam/25);
			}
			else{
				if(r<0.85){ 
					archi<<2<<endl;
					r1 = rand() % 3;
					r2 = rand() % int(tam/20);
				}
				else{ 
					archi<<3<<endl;
					r1 = rand() % 5;
					r2 = rand() % int(tam/15);
				}
			}
			archi<<"#RECURSOS"<<endl;
			cout<<"r1: "<<r1<<" "<<int(this->nRes/2)<<endl;
			for(int j=0;j<r1;j++){
				aux = rand() % this->nRes;
				archi<<aux+1<<endl;
			}
			archi<<"#TAREAS PREVIAS"<<endl;
			for(int j=0;j<r2;j++){
				aux = rand() % this->nTask;
				archi<<aux+1<<endl;	
			}
			archi<<"@"<<endl;
		}
	}
}
//-------------------------------------------------------------------------------------------------
